package ar.com.profertil.utils.service;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.Security;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;

import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import ar.com.profertil.utils.certificado.Base64;
import ar.com.profertil.utils.certificado.Certificado;

// TODO: Auto-generated Javadoc
/**
 * The Class RSABCAux.
 */
public class RSABCAux {

    /**
     * Desencriptar.
     *
     * @param input the input
     * @param output the output
     * @param usePrivate the use private
     * @return the string
     * @throws Exception the exception
     */
    public String desencriptar(String input, String output, boolean usePrivate) throws Exception {
        RSAKeyParameters parameters = null;
        if(usePrivate) {
        	parameters = new RSAPrivateCrtKeyParameters(mod, pubExp, privExp, p, q, pExp, qExp, crtCoef);
        } else {
        	parameters = new RSAKeyParameters(false, mod, pubExp);
        }
        AsymmetricBlockCipher eng = new RSAEngine();
        eng = new PKCS1Encoding(eng);
        eng.init(false, parameters);
        try {
            FileInputStream streamF = new FileInputStream(input);
            FileOutputStream fos = new FileOutputStream(output);
            int tamanoBuffer = eng.getInputBlockSize();
            byte arrayBytes[] = new byte[tamanoBuffer];
            int tamanoArchivo = streamF.available();
            int cantidad;
            for(; streamF.available() != 0 && streamF.available() > tamanoBuffer; System.out.print("Desencriptando: " + (100 - (cantidad * 100) / tamanoArchivo) + "% ")) {
                cantidad = streamF.available();
                streamF.read(arrayBytes, 0, tamanoBuffer);
                byte datosDesEncriptados[] = eng.processBlock(arrayBytes, 0, arrayBytes.length);
                fos.write(datosDesEncriptados, 0, datosDesEncriptados.length);
                System.out.print("\r");
            }

            streamF.read(arrayBytes, 0, tamanoBuffer);
            byte datosDesEncriptados[] = eng.processBlock(arrayBytes, 0, arrayBytes.length);
            fos.write(datosDesEncriptados, 0, datosDesEncriptados.length);
            
            fos.close();
            streamF.close();
            
            System.out.print("\r");
            System.out.print("Desencriptando: 100% ");
        } catch(Exception e) {
            throw e;
        }
        return "Desencripci\363n RSA Terminada.";
    }

    /**
     * Encriptar.
     *
     * @param input the input
     * @param output the output
     * @param usePrivate the use private
     * @return the string
     * @throws Exception the exception
     */
    public String encriptar(String input, String output, boolean usePrivate) throws Exception {
        RSAKeyParameters parameters = null;
        if(usePrivate) {
        	parameters = new RSAPrivateCrtKeyParameters(mod, pubExp, privExp, p, q, pExp, qExp, crtCoef);
        } else {
        	parameters = new RSAKeyParameters(false, mod, pubExp);
        }
        AsymmetricBlockCipher eng = new RSAEngine();
        eng = new PKCS1Encoding(eng);
        eng.init(true, parameters);
        try {
            FileInputStream streamF = new FileInputStream(input);
            FileOutputStream fos = new FileOutputStream(output);
            int tamanoBuffer = eng.getInputBlockSize();
            byte arrayBytes[] = new byte[tamanoBuffer];
            byte datosEncriptados[] = (byte[])null;
            int tamanoArchivo = streamF.available();
            int cantidad;
            for(; streamF.available() != 0; System.out.print("Encriptando: " + (100 - (cantidad * 100) / tamanoArchivo) + "% ")) {
                cantidad = streamF.available();
                if(cantidad <= tamanoBuffer - 1) {
                    int aux = streamF.available();
                    streamF.read(arrayBytes, 0, aux);
                    datosEncriptados = (byte[])null;
                    datosEncriptados = eng.processBlock(arrayBytes, 0, aux);
                    fos.write(datosEncriptados, 0, datosEncriptados.length);
                } else {
                    streamF.read(arrayBytes, 0, tamanoBuffer);
                    datosEncriptados = eng.processBlock(arrayBytes, 0, arrayBytes.length);
                    fos.write(datosEncriptados, 0, datosEncriptados.length);
                }
                System.out.print("\r");
            }

            fos.close();
            streamF.close();
            
            System.out.print("\r");
            System.out.print("Encriptando: 100% ");
        } catch(Exception e) {
            throw e;
        }
        return "- Encripci\363n RSA Terminada.";
    }

    /**
     * Encriptar archivo pub cert.
     *
     * @param encriptar the encriptar
     * @param inputFile the input file
     * @param outputFile the output file
     * @param certificado the certificado
     * @param keyStorageView the key storage view
     * @param sAlias the alias of a key in the key storage view
     * @param password the password
     * @throws Exception the exception
     */
    public void encriptarArchivoPubCert(boolean encriptar, String inputFile, String outputFile, String certificado, String keyStorageView, String sAlias, String password) throws Exception {
        try {
            Certificado cert = new Certificado(null, password, keyStorageView, sAlias, true);
            mod = cert.getClavePrivada().getModulus();
            pubExp = cert.getClavePrivada().getPublicExp();
            if(encriptar) { 
            	System.out.print(encriptar(inputFile, outputFile, false));
            } else {
            	System.out.print(desencriptar(inputFile, outputFile, false));
            }
        } catch(Exception e) {
            throw e;
        }
    }

    /**
     * Obtener m d5.
     *
     * @param inputFile the input file
     * @return the string
     * @throws Exception the exception
     */
    public static String obtenerMD5(String inputFile) throws Exception {
        Digest digest = new MD5Digest();
        byte resBuf[] = new byte[digest.getDigestSize()];
        try {
            FileInputStream streamF = new FileInputStream(inputFile);
            int tamanoBuffer = 1024;
            byte arrayBytes[] = new byte[tamanoBuffer];
            while(streamF.available() != 0) {
                int cantidad = streamF.available();
                if(cantidad <= tamanoBuffer - 1) {
                    int aux = streamF.available();
                    streamF.read(arrayBytes, 0, aux);
                    digest.update(arrayBytes, 0, aux);
                } else {
                    streamF.read(arrayBytes, 0, tamanoBuffer);
                    digest.update(arrayBytes, 0, tamanoBuffer);
                }
            }
            streamF.close();
        } catch(Exception e) {
//           throw e;
        }
        digest.doFinal(resBuf, 0);
        return new String(Hex.encode(resBuf));
    }

    /**
     * Encriptar string.
     *
     * @param encriptar the encriptar
     * @param cadena the cadena
     * @param usePrivate the use private
     * @return the string
     * @throws Exception the exception
     */
    public static String encriptarString(boolean encriptar, String cadena, boolean usePrivate) throws Exception {
        RSAKeyParameters parameters = null;
        if(usePrivate) {
        	parameters = new RSAPrivateCrtKeyParameters(mod, pubExp, privExp, p, q, pExp, qExp, crtCoef);
        } else {
        	parameters = new RSAKeyParameters(false, mod, pubExp);
        }
        byte data[] = (byte[])null;
        if(encriptar) {
        	data = cadena.getBytes();
        } else {
        	data = Hex.decode(cadena);
        }
        AsymmetricBlockCipher eng = new RSAEngine();
        eng = new PKCS1Encoding(eng);
        eng.init(encriptar, parameters);
        try {
            data = eng.processBlock(data, 0, data.length);
        } catch(Exception e) {
            throw e;
        }
        String pp = "";
        for(int i = 0; i < data.length; i++) {
        	pp = pp + (char)data[i];
        }

        if(encriptar) {
        	return new String(Hex.encode(data));
        } else {
        	return pp;
        }
    }

    /**
     * Encriptar string largo.
     *
     * @param input the input
     * @param certificado the certificado
     * @param keyStorageView the key storage view
     * @param sAlias the alias of a key in the key storage view
     * @param pass the pass
     * @return the string
     * @throws Exception the exception
     */
    public static String encriptarStringLargo(String input, String certificado, String keyStorageView, String sAlias, String pass) throws Exception {
        RSAKeyParameters parameters = null;
        String resultado = "";
        Certificado cert = new Certificado(null, pass, keyStorageView, sAlias, true);
        mod = cert.getClavePrivada().getModulus();
        pubExp = cert.getClavePrivada().getPublicExp();
        privExp = cert.getClavePrivada().getPrivateExp();
        p = cert.getClavePrivada().getPrimeP();
        q = cert.getClavePrivada().getPrimeQ();
        pExp = cert.getClavePrivada().getPrimeExpP();
        qExp = cert.getClavePrivada().getPrimeExpQ();
        crtCoef = cert.getClavePrivada().getCRTCoefficient();
        parameters = new RSAPrivateCrtKeyParameters(mod, pubExp, privExp, p, q, pExp, qExp, crtCoef);
        AsymmetricBlockCipher eng = new RSAEngine();
        eng = new PKCS1Encoding(eng);
        eng.init(true, parameters);
        try {
        	ByteArrayInputStream streamF = new ByteArrayInputStream(input.getBytes("UTF-8"));
            ByteArrayOutputStream fos = new ByteArrayOutputStream();
            int tamanoBuffer = eng.getInputBlockSize();
            byte arrayBytes[] = new byte[tamanoBuffer];
            byte datosEncriptados[] = (byte[])null;
            while(streamF.available() != 0) {
                int cantidad = streamF.available();
                if(cantidad <= tamanoBuffer - 1) {
                    int aux = streamF.available();
                    streamF.read(arrayBytes, 0, aux);
                    datosEncriptados = (byte[])null;
                    datosEncriptados = eng.processBlock(arrayBytes, 0, aux);
                    fos.write(datosEncriptados, 0, datosEncriptados.length);
                } else {
                    streamF.read(arrayBytes, 0, tamanoBuffer);
                    datosEncriptados = eng.processBlock(arrayBytes, 0, arrayBytes.length);
                    fos.write(datosEncriptados, 0, datosEncriptados.length);
                }
            }
            resultado = new String(Hex.encode(fos.toByteArray()));
        }
        catch(Exception e) {
            throw e;
        }
        return resultado;
    }

    /**
     * Desencriptar string largo.
     *
     * @param input the input
     * @param modulo the modulo
     * @param expPub the exp pub
     * @return the string
     * @throws Exception the exception
     */
    public static String desencriptarStringLargo(String input, String modulo, int expPub) throws Exception {
        RSAKeyParameters parameters = null;
        String stringDesencriptado = "";
        mod = new BigInteger(modulo);
        pubExp = new BigInteger("" + expPub);
        parameters = new RSAKeyParameters(false, mod, pubExp);
        AsymmetricBlockCipher eng = new RSAEngine();
        eng = new PKCS1Encoding(eng);
        eng.init(false, parameters);
        try {
            byte ba[] = Hex.decode(input);
            ByteArrayInputStream streamF = new ByteArrayInputStream(ba);
            ByteArrayOutputStream fos = new ByteArrayOutputStream();
            int tamanoBuffer = eng.getInputBlockSize();
            byte arrayBytes[] = new byte[tamanoBuffer];
            byte datosDesEncriptados[] = (byte[])null;
            while(streamF.available() != 0 && streamF.available() > tamanoBuffer) {
                streamF.read(arrayBytes, 0, tamanoBuffer);
                datosDesEncriptados = eng.processBlock(arrayBytes, 0, arrayBytes.length);
                fos.write(datosDesEncriptados, 0, datosDesEncriptados.length);
                for(int i = 0; i < datosDesEncriptados.length; i++) {
                	stringDesencriptado = stringDesencriptado + (char)datosDesEncriptados[i];
                }
            }
            streamF.read(arrayBytes, 0, tamanoBuffer);
            datosDesEncriptados = eng.processBlock(arrayBytes, 0, arrayBytes.length);
            fos.write(datosDesEncriptados, 0, datosDesEncriptados.length);
            for(int i = 0; i < datosDesEncriptados.length; i++) {
            	stringDesencriptado = stringDesencriptado + (char)datosDesEncriptados[i];
            }
        } catch(Exception e) {
            throw e;
        }
        return stringDesencriptado;
    }

    /**
     * Procesar string pub.
     *
     * @param encriptar the encriptar
     * @param modulo the modulo
     * @param expPub the exp pub
     * @param cadena the cadena
     * @return the string
     * @throws Exception the exception
     */
    public static String procesarStringPub(boolean encriptar, String modulo, int expPub, String cadena) throws Exception {
        mod = new BigInteger(modulo);
        pubExp = new BigInteger("" + expPub);
        return encriptarString(encriptar, cadena, false);
    }

    /**
     * Procesar string pub cert.
     *
     * @param encriptar the encriptar
     * @param certificado the certificado
     * @param keyStorageView the key storage view
     * @param sAlias the alias of a key in the key storage view
     * @param password the password
     * @param cadena the cadena
     * @return the string
     * @throws Exception the exception
     */
    public static String procesarStringPubCert(boolean encriptar, String certificado, String keyStorageView, String sAlias, String password, String cadena) throws Exception {
        Certificado cert = new Certificado(null, password, keyStorageView, sAlias, true);
        mod = cert.getClavePrivada().getModulus();
        pubExp = cert.getClavePrivada().getPublicExp();
        return encriptarString(encriptar, cadena, false);
    }

    /**
     * Procesar string pub cert.
     *
     * @param encriptar the encriptar
     * @param certificado the certificado
     * @param cadena the cadena
     * @return the string
     * @throws Exception the exception
     */
    public static String procesarStringPubCert(boolean encriptar, String certificado, String cadena) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        FileInputStream fis = new FileInputStream(certificado);
        CertificateFactory cf2 = CertificateFactory.getInstance("X.509");
        BufferedInputStream bis = new BufferedInputStream(fis);
        Certificate cert2 = cf2.generateCertificate(bis);
        RSAPublicKey key = (RSAPublicKey)cert2.getPublicKey();
        mod = key.getModulus();
        pubExp = key.getPublicExponent();
        return encriptarString(encriptar, cadena, false);
    }

    /**
     * Obtener datos publicos.
     *
     * @param certificado the certificado
     * @return the string
     * @throws Exception the exception
     */
    public static String obtenerDatosPublicos(InputStream certificado) throws Exception  {
    	Security.addProvider(new BouncyCastleProvider());
    	CertificateFactory cf2 = CertificateFactory.getInstance("X.509");
    	BufferedInputStream bis = new BufferedInputStream(certificado);
    	X509Certificate cert2 = (X509Certificate)cf2.generateCertificate(bis);
    	RSAPublicKey key = (RSAPublicKey)cert2.getPublicKey();
    	System.out.print(key);
    	mod = key.getModulus();
    	pubExp = key.getPublicExponent();
    	return mod + "-" + pubExp + "-" + cert2.getSubjectDN();
    }

    /**
     * Procesar string priv.
     *
     * @param encriptar the encriptar
     * @param certificado the certificado
     * @param keyStorageView the key storage view
     * @param sAlias the alias of a key in the key storage view
     * @param password the password
     * @param cadena the cadena
     * @return the string
     * @throws Exception the exception
     */
    public static String procesarStringPriv(boolean encriptar, String certificado, String keyStorageView, String sAlias, String password, String cadena) throws Exception {
        Certificado cert = new Certificado(null, password, keyStorageView, sAlias, true);
        mod = cert.getClavePrivada().getModulus();
        pubExp = cert.getClavePrivada().getPublicExp();
        privExp = cert.getClavePrivada().getPrivateExp();
        p = cert.getClavePrivada().getPrimeP();
        q = cert.getClavePrivada().getPrimeQ();
        pExp = cert.getClavePrivada().getPrimeExpP();
        qExp = cert.getClavePrivada().getPrimeExpQ();
        crtCoef = cert.getClavePrivada().getCRTCoefficient();
        return encriptarString(encriptar, cadena, true);
    }

    /**
     * Obtener m d5 string.
     *
     * @param cadena the cadena
     * @return the string
     * @throws Exception the exception
     */
    public static String obtenerMD5String(String cadena) throws Exception {
        Digest digest = new MD5Digest();
        byte resBuf[] = new byte[digest.getDigestSize()];
        digest.update(cadena.getBytes(), 0, cadena.getBytes().length);
        digest.doFinal(resBuf, 0);
        return new String(Hex.encode(resBuf));
    }

    /**
     * Obtener sha string.
     *
     * @param cadena the cadena
     * @return the string
     * @throws Exception the exception
     */
    public static String obtenerSHAString(String cadena) throws Exception {
        Digest digest = new SHA1Digest();
        byte resBuf[] = new byte[digest.getDigestSize()];
        digest.update(cadena.getBytes(), 0, cadena.getBytes().length);
        digest.doFinal(resBuf, 0);
        return new String(Hex.encode(resBuf));
    }

    /**
     * Firmar.
     *
     * @param certificado the certificado
     * @param keyStorageView the key storage view
     * @param sAlias the alias of a key in the key storage view
     * @param password the password
     * @param cadena the cadena
     * @return the string
     * @throws Exception the exception
     */
    public static String firmar(String certificado, String keyStorageView, String sAlias, String password, String cadena) throws Exception {
        Certificado cert = new Certificado(null, password, keyStorageView, sAlias, true);
        Signature signer = Signature.getInstance("SHA1withRSA");
        signer.initSign(cert.getClavePura());
        signer.update(cadena.getBytes());
        return new Base64().encode(signer.sign());
    }

    /** The mod. */
    private static BigInteger mod = null;
    
    /** The pub exp. */
    private static BigInteger pubExp = null;
    
    /** The priv exp. */
    private static BigInteger privExp = null;
    
    /** The p. */
    private static BigInteger p = null;
    
    /** The q. */
    private static BigInteger q = null;
    
    /** The p exp. */
    private static BigInteger pExp = null;
    
    /** The q exp. */
    private static BigInteger qExp = null;
    
    /** The crt coef. */
    private static BigInteger crtCoef = null;

}
